package cn.xinfei.xdecision.data.runner.api;


import cn.hutool.core.collection.CollectionUtil;
import cn.xinfei.xdecision.common.migrate.ElementMapper;
import cn.xinfei.xdecision.common.model.common.ResponseEntityDto;
import cn.xinfei.xdecision.common.model.datax.consts.VarPrometheusTagsConst;
import cn.xinfei.xdecision.common.model.datax.consts.VariableConst;
import cn.xinfei.xdecision.common.model.datax.datamanage.request.VarBussinessLabelReqVO;
import cn.xinfei.xdecision.common.model.datax.enums.CollectStageEnum;
import cn.xinfei.xdecision.common.model.datax.enums.MigrateEnum;
import cn.xinfei.xdecision.common.model.datax.enums.VarCatTypeEnum;
import cn.xinfei.xdecision.common.model.datax.enums.VariableStrategyType;


import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataException;
import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataExceptionType;
import cn.xinfei.xdecision.common.model.datax.migrate.Element;
import cn.xinfei.xdecision.common.service.datax.datamanage.v2.VariableService;
import cn.xinfei.xdecision.data.core.frame.executors.collect.credit.ICreditCollect;
import cn.xinfei.xdecision.data.core.frame.executors.collect.pre.IPreCollect;
import cn.xinfei.xdecision.data.core.frame.executors.collect.real.IRealCollect;
import cn.xinfei.xdecision.data.core.frame.listener.CreditQueryListener;
import cn.xinfei.xdecision.data.core.frame.strategy.StrategyContext;
import cn.xinfei.xdecision.data.core.frame.strategy.VariableStrategy;
import cn.xinfei.xdecision.data.core.migrate.MigrateVarService;
import cn.xinfei.xdecision.data.core.trace.VarTrace;
import cn.xinfei.xdecision.data.core.vo.credit.CreditStatusDTO;
import cn.xinfei.xdecision.data.core.vo.migrate.MigrateVarReqVO;
import cn.xinfei.xdecision.model.*;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Transaction;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.repository.query.Param;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;


@Slf4j
@RestController
@RequestMapping("data")
public class RiskDataApi {


    @Autowired
    private IPreCollect preCollect;

    @Autowired
    private IRealCollect realCollect;

    @Autowired
    private StrategyContext strategyContext;

    @Autowired
    private ICreditCollect creditCollect;


    @Autowired
    private MeterRegistry meterRegistry;

    @Resource
    ElementMapper elementMapper;

    @Autowired
    private MigrateVarService migrateVarService;

    @Autowired
    VariableService variableService;

    @Value("${xdecision.data.label.bussiness.orgid}")
    Long bussinessLabelOrgId;




    @VarTrace(traceScene = VarTrace.TraceScene.PRE_COLLECT)
    @RequestMapping(value = "/preCollect", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    public VariableApiResponse preCollect(@RequestBody VariableApiRequest variableApiRequest) {
        VariableApiResponse response = new VariableApiResponse();
        Transaction catT = Cat.newTransaction(VarCatTypeEnum.PRE_COLLECT.getCode(), VarCatTypeEnum.PRE_COLLECT.getName());
        MDC.put(VariableConst.COLLECT_STAGE, VarCatTypeEnum.PRE_COLLECT.name());
        long startTime = System.currentTimeMillis();
        try {
            response = preCollect.preCollect(variableApiRequest);
            catT.setStatus(Transaction.SUCCESS);
            meterRegistry.counter("xengine_pre_collect_total", VarPrometheusTagsConst.VAR_API_TOTAL_TAGS.and("code", response.getCode())).increment();
        } catch (Exception e) {
            log.error("error in preCollect requestId: {}", variableApiRequest.getRequestId(), e);
            response.setCode("20000");
            response.setRequestId(variableApiRequest.getRequestId());
            response.setStage(CollectStageEnum.PRE_COLLECT.name());
            response.setMessage(e.getMessage());
            meterRegistry.counter("xengine_pre_collect_total", VarPrometheusTagsConst.VAR_API_TOTAL_TAGS.and("code", response.getCode())).increment();
            Cat.logError(e);
            catT.setStatus(e);
        } finally {
            Metrics.gauge("xengine_data_pre_collect_duration", VarPrometheusTagsConst.VAR_API_DURATION_TAGS, System.currentTimeMillis() - startTime);
            catT.complete();
        }
        return response;
    }

    @VarTrace(traceScene = VarTrace.TraceScene.REAL_COLLECT)
    @RequestMapping(value = "/realCollect", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    public VariableApiResponse realCollect(@RequestBody VariableApiRequest variableApiRequest) {
        VariableApiResponse response = new VariableApiResponse();
        Transaction catT = Cat.newTransaction(VarCatTypeEnum.REAL_COLLECT.getCode(), VarCatTypeEnum.REAL_COLLECT.getName());
        MDC.put(VariableConst.COLLECT_STAGE, VarCatTypeEnum.REAL_COLLECT.name());
        long startTime = System.currentTimeMillis();
        try {
            response = realCollect.realCollect(variableApiRequest);
            Metrics.counter(VarPrometheusTagsConst.LABEL_DATASOURCE_SUCCESS_COUNT,
                    VarPrometheusTagsConst.MetricsFliedTags.REQUEST_GROUP, variableApiRequest.getGroup()).increment();
            catT.setStatus(Transaction.SUCCESS);
            meterRegistry.counter("xengine_real_collect_total", VarPrometheusTagsConst.VAR_API_TOTAL_TAGS.and("code", response.getCode())).increment();
        } catch (Exception e) {
            log.error("error in realCollect requestId: {}", variableApiRequest.getRequestId(), e);
            response.setCode("20000");
            response.setRequestId(variableApiRequest.getRequestId());
            response.setStage(CollectStageEnum.REAL_TIME_COLLECT.name());
            response.setMessage(e.getMessage());
            Metrics.counter(VarPrometheusTagsConst.LABEL_DATASOURCE_FAIL_COUNT,
                    VarPrometheusTagsConst.MetricsFliedTags.REQUEST_GROUP, variableApiRequest.getGroup()).increment();
            meterRegistry.counter("xengine_real_collect_total", VarPrometheusTagsConst.VAR_API_TOTAL_TAGS.and("code", response.getCode())).increment();
            Cat.logError(e);
            catT.setStatus(e);
        } finally {
            Metrics.counter(VarPrometheusTagsConst.LABEL_DATASOURCE_REQUEST_COUNT,
                    VarPrometheusTagsConst.MetricsFliedTags.REQUEST_GROUP, variableApiRequest.getGroup()).increment();
            long endTime = System.currentTimeMillis();
            Set<Tag> tagList = new HashSet<>();
            tagList.add(Tag.of(VarPrometheusTagsConst.MetricsFliedTags.REQUEST_GROUP,  variableApiRequest.getGroup()));
            Metrics.gauge(VarPrometheusTagsConst.LABEL_DATASOURCE_DURATION, tagList, endTime - startTime);
            Metrics.gauge("xengine_data_real_collect_duration", VarPrometheusTagsConst.VAR_API_DURATION_TAGS, System.currentTimeMillis() - startTime);
            catT.complete();
        }
        return response;

    }

    @VarTrace(traceScene = VarTrace.TraceScene.CREDIT_UPLOAD)
    @RequestMapping(value = "/creditCollect", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    public CreditVarResponse creditCollect(@RequestBody CreditVarRequest creditVarRequest) {
        CreditVarResponse response = new CreditVarResponse();
        Transaction catT = Cat.newTransaction(VarCatTypeEnum.CREDIT_COLLECT.getCode(), VarCatTypeEnum.CREDIT_COLLECT.getName());
        MDC.put(VariableConst.COLLECT_STAGE, VarCatTypeEnum.CREDIT_COLLECT.name());
        long startTime = System.currentTimeMillis();
        try {
            VariableStrategy strategy = strategyContext.getStrategy(VariableStrategyType.CREDIT.name());
            response = (CreditVarResponse) strategy.process(creditVarRequest);
            catT.setStatus(Transaction.SUCCESS);
            meterRegistry.counter("xengine_credit_collect_total", VarPrometheusTagsConst.VAR_API_TOTAL_TAGS.and("code", response.getCode())).increment();
        } catch (Exception e) {
            log.error("error in creditCollect requestId: {}", creditVarRequest.getRequestId(), e);
            response.setCode("20000");
            response.setRequestId(creditVarRequest.getRequestId());
            response.setMessage(e.getMessage());
            meterRegistry.counter("xengine_credit_collect_total", VarPrometheusTagsConst.VAR_API_TOTAL_TAGS.and("code", response.getCode())).increment();
            Cat.logError(e);
            catT.setStatus(e);
        } finally {
            Metrics.gauge("xengine_data_collect_upload_duration", VarPrometheusTagsConst.VAR_API_DURATION_TAGS, System.currentTimeMillis() - startTime);
            catT.complete();
        }
        return response;
    }

    @VarTrace(traceScene = VarTrace.TraceScene.CREDIT_FETCH)
    @RequestMapping(value = "/fetchCreditVars", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    public VariableApiResponse fetchCreditVars(@RequestBody @Valid VariableApiRequest variableApiRequest) {
        VariableApiResponse response = new VariableApiResponse();
        Transaction catT = Cat.newTransaction(VarCatTypeEnum.CREDIT_FETCH.getCode(), VarCatTypeEnum.CREDIT_FETCH.getName());
        MDC.put(VariableConst.COLLECT_STAGE, VarCatTypeEnum.CREDIT_FETCH.name());
        long startTime = System.currentTimeMillis();
        try {
            response = creditCollect.fetchCreditVars(variableApiRequest);
            catT.setStatus(Transaction.SUCCESS);
            meterRegistry.counter("xengine_fetch_credit_total", VarPrometheusTagsConst.VAR_API_TOTAL_TAGS.and("code", response.getCode())).increment();
        } catch (Exception e) {
            log.error("error in fetchCreditVars requestId: {}", variableApiRequest.getRequestId(), e);
            response.setCode("20000");
            response.setRequestId(variableApiRequest.getRequestId());
            response.setStage(CollectStageEnum.REAL_TIME_COLLECT.name());
            response.setMessage(e.getMessage());
            meterRegistry.counter("xengine_fetch_credit_total", VarPrometheusTagsConst.VAR_API_TOTAL_TAGS.and("code", response.getCode())).increment();
            Cat.logError(e);
            catT.setStatus(e);
        } finally {
            Metrics.gauge("xengine_data_credit_fetch_duration", VarPrometheusTagsConst.VAR_API_DURATION_TAGS, System.currentTimeMillis() - startTime);
            catT.complete();
        }
        return response;
    }


    @PostMapping("migrateVars")
    public ResponseEntityDto<Object> migrateVars(@RequestBody MigrateVarReqVO migrateVarReqVO) {

        long startTime = System.currentTimeMillis();

        List<Element> javaVars = elementMapper.getAllElesBySystem("java");
        List<Element> pythonVars = elementMapper.getAllElesBySystem("python");
        List<Element> pythonModelVars = elementMapper.getAllModelElements();
        List<Element> modelCacheVars = elementMapper.getAllModelCacheElements();


        Map<String, List<Element>> fromPythonElesMap = pythonVars.stream().collect(Collectors.groupingBy(Element::getFrom));
        List<Element> pythonFuncVars = fromPythonElesMap.get("function");
        List<Element> pythonInputVars = fromPythonElesMap.get("input");
        List<Element> pythonOutputVars = fromPythonElesMap.get("output");
        List<Element> pythonInnerVars = fromPythonElesMap.get("ruleset");


        Map<String, List<Element>> fromJavaElesMap = javaVars.stream().collect(Collectors.groupingBy(Element::getFrom));
        List<Element> javaFuncAndComboVars = (List<Element>) CollectionUtil.union(fromJavaElesMap.get("function"), fromJavaElesMap.get("combo"));
        List<Element> javaFuncVars = fromJavaElesMap.get("function");
        List<Element> javaComboVars = fromJavaElesMap.get("combo");
        List<Element> javaInputVars = fromJavaElesMap.get("input");
        List<Element> javaOutputVars = fromJavaElesMap.get("output");
        List<Element> javaInnerVars = fromJavaElesMap.get("ruleset");


        List<List<String>> pythonThirdPartyVars = migrateVarService.getPythonThirdPartyVarsFromJsonFile();
        List<List<String>> pythonThirdPartyCacheVars = migrateVarService.getPythonThirdPartyCacheVarsFromJsonFile();
        List<List<String>> pythonBizVars = migrateVarService.getPythonBizVarsFromJsonFile();

        MigrateEnum migrateEnum = MigrateEnum.fromName(migrateVarReqVO.getVarMigrateType());
        switch (migrateEnum) {
            case JAVA_BIZ:
                List<String> allJavaBizVarCode = migrateVarService.getAllJavaBizVarCode(javaFuncAndComboVars);
                migrateVarService.migrateJavaBizVars(javaFuncVars, allJavaBizVarCode, migrateVarReqVO);
                log.info("migrate java_biz vars success!");
                break;
            case JAVA_BASIC:
                migrateVarService.migrateJavaBasicVars(javaInputVars, javaOutputVars, javaInnerVars, migrateVarReqVO);
                log.info("migrate java_basic vars success!");
                break;
            case JAVA_THIRDPARTY:
                migrateVarService.migrateJavaThirdPartyVars(javaFuncVars, migrateVarReqVO);
                log.info("migrate java_thridparty vars success!");
                break;
            case JAVA_COMBO:
                migrateVarService.migrateJavaComboVars(javaComboVars, migrateVarReqVO);
                log.info("migrate java_combo vars success!");
                break;
            case PYTHON_BIZ:
                migrateVarService.migratePythonBizVars(pythonFuncVars, pythonBizVars, migrateVarReqVO);
                log.info("migrate python_biz vars success!");
                break;
            case PYTHON_BASIC:
                migrateVarService.migratePythonBasicVars(pythonInputVars, pythonOutputVars, pythonInnerVars, migrateVarReqVO);
                log.info("migrate python_basic vars success!");
                break;
            case PYTHON_THIRDPARTY:
                migrateVarService.migratePythonThirdPartyVars(pythonFuncVars, pythonThirdPartyVars, migrateVarReqVO);
                log.info("migrate python_thridparty vars success!");
                break;

            case PYTHON_THIRDPARTY_CACHE:
                migrateVarService.migratePythonThirdPartyVars(pythonFuncVars, pythonThirdPartyCacheVars, migrateVarReqVO);
                log.info("migrate python_thridparty_cache vars success!");
                break;

            case PYTHON_MODEL:
                migrateVarService.migratePythonModelVars(pythonModelVars, migrateVarReqVO);
                log.info("migrate python_model vars success!");
                break;

            case PYTHON_MODEL_CACHE:
                migrateVarService.migratePythonModelVars(modelCacheVars, migrateVarReqVO);
                log.info("migrate python_model_cache vars success!");
                break;

            case VAR_PARAM:
                migrateVarService.migrateVarParams();
                log.info("migrate varParams success!");
                break;
            default:
                throw new XDecisionDataException(XDecisionDataExceptionType.DATA_NO_SUCH_MIGRATE_TYPE_ERROR);
        }
        long endTime = System.currentTimeMillis();
        log.info("stop varMigrateType ={}...timeConsuming ={}", migrateVarReqVO.getVarMigrateType(), endTime - startTime);
        return ResponseEntityDto.success(migrateVarReqVO.getVarMigrateType() + " 成功!");
    }

    @PostMapping("findBussinessVarsByOrganId")
    public ResponseEntityDto<Object> findBussinessVarsByOrganId(@RequestBody @Validated VarBussinessLabelReqVO varBussinessLabelReqVO) {
        return ResponseEntityDto.success(variableService.findBussinessVarsByOrganId(bussinessLabelOrgId));
    }

}
